package com.andrewshu.android.reddit.login; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import org.apache.http.HttpEntity; import org.apache.http.HttpException; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.cookie.Cookie; import org.apache.http.message.BasicNameValuePair; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; import org.apache.http.protocol.HTTP; import org.codehaus.jackson.JsonFactory; import org.codehaus.jackson.JsonParser; import org.codehaus.jackson.JsonToken; import com.andrewshu.android.reddit.common.CacheInfo; import com.andrewshu.android.reddit.common.Common; import com.andrewshu.android.reddit.common.Constants; import com.andrewshu.android.reddit.common.RedditIsFunHttpClientFactory; import com.andrewshu.android.reddit.common.util.StringUtils; import com.andrewshu.android.reddit.settings.RedditSettings; import android.content.Context; import android.os.AsyncTask; import android.util.Log; import android.webkit.CookieSyncManager; public class LoginTask extends AsyncTask<Void, Void, Boolean> { private static final String TAG = "LoginTask"; protected String mUsername; private String mPassword; protected String mUserError = null; private RedditSettings mSettings; private HttpClient mClient; private Context mContext; protected LoginTask(String username, String password, RedditSettings settings, HttpClient client, Context context) { mUsername = username; mPassword = password; mSettings = settings; mClient = client; mContext = context; } @Override public Boolean doInBackground(Void... v) { return doLogin(mUsername, mPassword, mSettings, mClient, mContext); } /** * On success stores the session cookie and modhash in your RedditSettings. * On failure does not modify RedditSettings. * Should be called from a background thread. * @return Error message, or null on success */ private Boolean doLogin(String username, String password, RedditSettings settings, HttpClient client, Context context) { String status = ""; String userError = "Error logging in. Please try again."; HttpEntity entity = null; try { // Construct data List<NameValuePair> nvps = new ArrayList<NameValuePair>(); nvps.add(new BasicNameValuePair("user", username.toString())); nvps.add(new BasicNameValuePair("passwd", password.toString())); nvps.add(new BasicNameValuePair("api_type", "json")); HttpPost httppost = new HttpPost(Constants.REDDIT_LOGIN_URL); httppost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8)); // Set timeout to 45 seconds for login HttpParams params = httppost.getParams(); HttpConnectionParams.setConnectionTimeout(params, 45000); HttpConnectionParams.setSoTimeout(params, 45000); // Perform the HTTP POST request HttpResponse response = client.execute(httppost); status = response.getStatusLine().toString(); if (!status.contains("OK")) { throw new HttpException(status); } entity = response.getEntity(); BufferedReader in = new BufferedReader(new InputStreamReader(entity.getContent())); String line = in.readLine(); in.close(); entity.consumeContent(); if (StringUtils.isEmpty(line)) { throw new HttpException("No content returned from login POST"); } if (Constants.LOGGING) Common.logDLong(TAG, line); if (RedditIsFunHttpClientFactory.getCookieStore().getCookies().isEmpty()) throw new HttpException("Failed to login: No cookies"); final JsonFactory jsonFactory = new JsonFactory(); final JsonParser jp = jsonFactory.createJsonParser(line); // Go to the errors while (jp.nextToken() != JsonToken.FIELD_NAME || !Constants.JSON_ERRORS.equals(jp.getCurrentName())) ; if (jp.nextToken() != JsonToken.START_ARRAY) throw new IllegalStateException("Login: expecting errors START_ARRAY"); if (jp.nextToken() != JsonToken.END_ARRAY) { if (line.contains("WRONG_PASSWORD")) { userError = "Bad password."; throw new Exception("Wrong password"); } else { // Could parse for error code and error description but using whole line is easier. throw new Exception(line); } } // Getting here means you successfully logged in. // Congratulations! // You are a true reddit master! // Get modhash while (jp.nextToken() != JsonToken.FIELD_NAME || !Constants.JSON_MODHASH.equals(jp.getCurrentName())) ; jp.nextToken(); settings.setModhash(jp.getText()); // Could grab cookie from JSON too, but it lacks expiration date and stuff. So grab from HttpClient. List<Cookie> cookies = RedditIsFunHttpClientFactory.getCookieStore().getCookies(); for (Cookie c : cookies) { if (c.getName().equals("reddit_session")) { settings.setRedditSessionCookie(c); break; } } settings.setUsername(username); CookieSyncManager.getInstance().sync(); CacheInfo.invalidateAllCaches(context); return true; } catch (Exception e) { mUserError = userError; if (entity != null) { try { entity.consumeContent(); } catch (Exception e2) { if (Constants.LOGGING) Log.e(TAG, "entity.consumeContent()", e); } } if (Constants.LOGGING) Log.e(TAG, "doLogin()", e); } return false; } }